#> pip3 install pymodbus==2.5.3
from pymodbus.client.sync import ModbusSerialClient


import  queue
import  threading
import  time
class TCoModbus:
    def __init__(self):
        self.config ={
            "ComPort":"COM3",
            "Baud":38400
        }
        self.events={
            "onReadTask":None,
            "onReadAll":None
        }
        self.modbusClient = None
        self.readTasks={}
        self.readTaskIndex = 0
        self.registers ={}
        self.dps = {}
        self.writeBuf = queue.Queue()
        self.coThread = None
    def initDatas(self):
        try:
            pass
        except Exception as er:
            print(er)
    def start(self):
        try:
            self.stop()
            self.initDatas()
            comPort = self.config['ComPort']
            baud = self.config["Baud"]
            client = ModbusSerialClient(method='rtu', port=comPort, baudrate=baud)
            self.modbusClient = client
            taskList = self.readTasks.values()
            taskNum = len(taskList)
            while( 1>0):
                try:
                    if self.writeBuf.empty():
                        task = taskList[self.readTaskIndex]
                        try:
                            funCode = task["funCode"]
                            unitId = task["unitId"]
                            addrFrom = task["addrFrom"]
                            addrNum = task["addrNum"]
                            bitFlag = task["bitFlag"]
                            if funCode == 0x03 :
                                holdRegs =  client.read_holding_registers(addrFrom , addrNum , unit = unitId)
                                if holdRegs.isError():
                                    print('read nodbus hold regist faild')
                                else:
                                    for i in range(len(holdRegs.registers)):
                                        addr = i + addrFrom
                                        v = holdRegs.registers[i]
                                        bytes = (v).to_bytes(2 , byteorder='big')
                                        reg = {
                                            "addr" : i + addrFrom,
                                            "index": i ,
                                            "v": v,
                                            "h":bytes[0],
                                            "l": bytes[1],
                                            "bs":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
                                        }
                                        self.registers[addr] = reg
                                        if bitFlag>0:
                                            for i in range(8):
                                                ha = (bytes[0])>>1
                                                la = (bytes[1])>>1
                                                reg['bs'][i] = la % 2
                                                reg['bs'][i+8] = ha % 2
                                    self.decodeReadTask( task)
                                    if self.events['onReadTask']!=None:
                                        try:
                                            self.events['onReadTask'](task)
                                        except Exception as erEvents:
                                            print(erEvents)
                        except Exception as ee:
                            print(ee)
                        self.readTaskIndex = self.readTaskIndex + 1
                        if self.readTaskIndex>= taskNum:
                            self.readTaskIndex = 0
                            if self.events['onReadAll']!=None:
                                try:
                                    self.events['onReadAll'](self)
                                except Exception as erEvents:
                                    print(erEvents)

                except Exception as eer:
                    print(eer)
                    break
            self.stop()
        except Exception as er:
            print(er)
    def startAsnyc(self):
        try:
            self.coThread = threading.Thread(target=self.start , daemon=True)
            self.coThread.start()
        except Exception as er:
            print(er)
    def decodeReadTask(self , task):
        try:
            dps = task.dps
            for dp in dps:
                try:
                    dpType = dp["dpType"]
                    dpBitFrom = dp["bitFrom"]
                    dpBitLen = dp["bitLen"]
                    regAddr = dp["addr"]
                    if regAddr in  self.registers.keys():
                        reg = self.registers[regAddr]
                        v =0
                        if dpType == 'V01' or dpType == 'V02':
                            if regAddr in  self.registers.keys():
                                v = int( "".join( [str(x) for x in  reg.bs[dpBitFrom: dpBitFrom+dpBitLen]]),2)
                            elif dpType == 'V03':
                                v = int.from_bytes(bytearray([reg['h'], reg['l']]) , byteorder='big' , signed= True)
                            elif dpType == 'V04':
                                v = reg['v']
                            elif dpType == 'V05':
                                if (regAddr+1) in  self.registers.keys() :
                                    nextReg = self.registers[regAddr+1]
                                    bytes = bytearray([reg["h"],reg["l"],nextReg["h"],nextReg["l"]])
                                    v = int.from_bytes(bytes, byteorder='big' , signed=False)
                            elif dpType == 'V09':
                                if (regAddr+1) in  self.registers.keys() :
                                    nextReg = self.registers[regAddr+1]
                                    bytes = bytearray([reg["h"],reg["l"],nextReg["h"],nextReg["l"]])
                                    v = int.from_bytes(bytes, byteorder='big' , signed=True)
                            elif dpType == 'V06':
                                v = reg['h']
                            elif dpType == 'V07':
                                v = reg['l']
                            elif dpType == 'V08':
                                v = reg['v']
                            dp['ov'] = dp['nv']
                            dp['nv'] = v
                            dp['tm'] = time.time()
                except Exception as errDecode:
                    print(errDecode)
        except Exception as er:
            print(er)
    def getTaskChangedDps(self, task):
        dps = []
        try:
            for dp in task.dps:
                tm = dp["tm"]
                nv = dp['nv']
                ov = dp['ov']
                notifyType = dp['notifyType']
                notifyThreshold = dp['notifyThreshold']
                flag = tm ==0
                if not flag:
                    if notifyType =='N01':
                        flag = nv!=ov
                    elif notifyType =='N02':
                        if ov == 0 and nv!=0:
                            flag = True
                        else:
                            diffRate = abs((nv - ov)*1.0/ov)
                            flag = diffRate > notifyThreshold
                    elif notifyType=='N03':
                        diff = abs(nv - ov)
                        flag = diff > notifyThreshold
                if flag:
                    dps.append(dp)
        except Exception as er:
            print(er)
        return dps
    def stop(self):
        try:
            if self.modbusClient!=None:
                self.modbusClient.close()
                self.modbusClient = None
        except Exception as er:
            print(er)